#!/usr/bin/env ruby

require 'Xml'
require 'Activity'
require 'DataObject'


class Bpel < Xml
  ACTIVITY = { :invoke => "invoke", :receive => "receive", :reply => "reply", :assign => "assign", :compensate => "compensate", :compensateScope => "compensateScope", :exit => "exit", :rethrow => "rethrow", :throw => "throw", :validate => "validate", :wait => "wait", :flow => "flow", :forEach => "forEach", :if => "if", :pick => "pick", :onAlarm => "onAlarm", :onMessage => "onMessage", :repeatUntil => "repeatUntil", :while => "while", :extensionActivity => "extensionActivity", :elseif => "elseif" 
               #, :empty => "empty"
               #, :scope => "scope"
               #, :sequence => "sequence" 
               }
  
  def activity_collect(node)
	act = Activity.new
	if act.type == ACTIVITY[:extensionActivity]
	  act.name = (node.child.nil?) ? node.name : node.child["name"]
	else
	  act.name = (node["name"].nil?) ? node.name : node["name"]
	end
	act.type = node.name
	act.description = documentation(node)
	act.reference = node['partnerLink']
	act.operation = node['operation']
	if act.type == ACTIVITY[:invoke]
	  act.input = [variable_data_by_name(node['inputVariable'])]
	  act.output = [variable_data_by_name(node['outputVariable'])] if !node['outputVariable'].nil?
	elsif act.type == ACTIVITY[:receive] or act.type == ACTIVITY[:reply]
	  act.input = [variable_data_by_name(node['variable'])]
	elsif act.type == ACTIVITY[:assign]
	  act.output = assign_to_variables_collect(node)
	  act.input = assign_from_variables_collect(node)
	elsif act.type == ACTIVITY[:if]
	  act.input = variables_names_in_text(node.xpath("#{node.path}/*[local-name()='condition']").text).collect{|name| variable_data_by_name(name)}.compact
	elsif act.type == ACTIVITY[:throw]
	  act.input = [variable_data_by_name(node['faultVariable'])] if !node['faultVariable'].nil?
	elsif act.type == ACTIVITY[:validate]
	  act.input = node['variables'].split(" ").collect{|name| variable_data_by_name(name)}
	elsif act.type == ACTIVITY[:wait] or act.type == ACTIVITY[:onAlarm]
	  act.input = variables_names_in_text(node.xpath("#{node.path}/*[contains('for until',local-name())]").text).collect{|name| variable_data_by_name(name)}.compact
	elsif act.type == ACTIVITY[:forEach]
	  act.input = node.xpath("#{node.path}/*[contains('startCounterValue finalCounterValue',local-name())]").collect{|node| variable_data_by_name(variables_names_in_text(node.text))}.compact
	elsif act.type == ACTIVITY[:onMessage]
	  act.input = [variable_data_by_name(node['variable'])]
	elsif act.type == ACTIVITY[:repeatUntil] or act.type == ACTIVITY[:while]
	  act.input = variables_names_in_text(node.xpath("#{node.path}/*[contains('condition',local-name())]").text).collect{|name| variable_data_by_name(name)}.compact
	elsif act.type == ACTIVITY[:extensionActivity]
	  act.input = variables_names_in_text(node.xpath("#{node.path}/*[contains('condition',local-name())]").text).collect{|name| variable_data_by_name(name)}.compact
	end
	act
  end

  def activities
    activities_doc.collect do |el|
      activity_collect el
    end
  end

  def variables_names_in_text(text)
      text.scan(/[$][\w]*/).collect do |name|
	name.slice(1,999)
      end.uniq.compact
  end
  
  def assign_from_variables_collect(node)
    var = []
    node.xpath("#{node.path}//*[local-name()='from']/@variable").each do |el|
      var << el.value
    end
    node.xpath("#{node.path}//*[local-name()='from']").each do |el|
	var = var + variables_names_in_text(el.text)
    end
    var.uniq.collect do |el|
	variable_data_by_name el
    end.compact
  end
  
  def assign_to_variables_collect(node)
    var = []
    node.xpath("#{node.path}//*[local-name()='to']/@variable").each do |el|
      var << el.value
    end
    node.xpath("#{node.path}//*[local-name()='to']").each do |el|
	var = var + variables_names_in_text(el.text)
    end
    var.uniq.collect do |el|
	 variable_data_by_name el
    end.compact
  end
  
  def activities_doc
    doc.xpath('//*').collect do |el|
      if ACTIVITY.has_value? el.name
	el
      end
    end.compact
  end

  def variables_doc
    @doc.xpath("//*[local-name()='variable']")
  end

  def variables_node(node=nil)
    path = node_path(node)
    @doc.xpath("#{(path.last.eql?("/"))?"":(path)}/*/*[local-name()='variable']")
  end

  def variable_to_data(node)
      var = DataObject.new
      var.name = node["name"]
      type = node.xpath("#{node.path}/@*[contains('messageType type element',local-name())]")[0]
      if type.text.include?(":")
	var.type = type.text.split(":")[1]
      else
	var.type = type.text
      end
      var.namespace = @doc.namespaces[XMLNS+":"+type.text.split(":")[0]]
      var.resource_type = type.name
      var.description = documentation(node)
      var
  end

  def variables_all
    variables_doc.collect do |el|
      var = variable_to_data(el)
    end
  end

  def variables(node)
    variables_node(node).collect do |el|
      var = variable_to_data(el)
    end
  end

  def variables_accessible(node=nil)
    node = @doc.elements[0] if node.nil?
    var = variables(node)
    until node_document?(node)
      node = node.parent
      if var.nil?
	var = variables(node)
      else
	var.concat variables(node)
      end
    end
    var
  end

  def variable_data_by_name(name)
    variables_all.select{|v| v.name == name}[0]
  end

  def type
    BPEL
  end

end

